package projorientado.image;

import java.awt.Rectangle;
import java.util.Stack;
import java.util.Vector;

/**
 *  Sistema de segmentação completa de uma imagem.<BR>
 *  Separa todos objetos ligados pela mesma comparação com coeficiente, isto é, 
 *  todos os valores < coef vão para o mesmo segmento.
 *  Assim como todos valores >= coef vão para o mesmo segmento.<BR>
 *  A segmentação é feita retornando apenas os segmentos dentro de um tamanho definido.
 */
public class FullSegmentation {

    private static class SinglePoint {
        public int x,y;
        public SinglePoint() {}
        public SinglePoint(int x,int y) {this.x=x;this.y=y;}
    }
    
    /**
     * Filtra todos segmentos de uma imagem, retornando os segmentos de interesse.<BR>
     * Os segmentos de interesse são aqueles que estão dentro das proporções especificadas.
     * @param image imagem de entrada
     * @param coef coeficiente de comparação
     * @param minWidth menor largura
     * @param maxWidth maior largura
     * @param minHeight menor altura
     * @param maxHeight maior altura
     * @return vetor de segmentos filtrados da imagem
     */
    public static Vector<Segment> consumeSegments(ImageLayerData image, float coef,
            int minWidth, int maxWidth, int minHeight, int maxHeight) {
        
        int width = image.getWidth();
        int height = image.getHeight();
        
        Vector<SinglePoint> points = new Vector();
        Stack<SinglePoint> aux = new Stack();
        Vector<Segment> ret = new Vector();
        
        for(int x = 0; x < width; x++) {
            for(int y = 0; y < height; y++) {
                Rectangle rect = consumeSegment(image,coef,x,y,points,aux);
                if(rect != null && 
                        rect.getWidth() >= minWidth && rect.getWidth() <= maxWidth &&
                        rect.getHeight() >= minHeight && rect.getHeight() <= maxHeight) {
                    
                    ret.add(buildSegment(rect,points));
                }
            }
        }
        
        return ret;
    }
    
    private static Segment buildSegment(Rectangle rect, Vector<SinglePoint> points) {
        int w = (int)rect.getWidth(), h = (int)rect.getHeight();
        int tx = (int)rect.getX(), ty = (int)rect.getY();
        ImageLayerData iret = new ImageLayerData(w,h);
        for(int x = 0; x < w; x++)
            for(int y = 0; y < h; y++)
                iret.setPixel(x, y, 1f);
        
        for(int i = 0; i < points.size(); i++) {
            SinglePoint p = points.elementAt(i);
            iret.setPixel(p.x-tx, p.y-ty, 0f);
        }
        
        return new Segment(iret,rect);
    }
    
    private static Rectangle consumeSegment(ImageLayerData image, float coef, int x, int y, Vector<SinglePoint> points, Stack<SinglePoint> aux) {
        points.clear();
        aux.clear();
        if(image.getPixel(x, y) < coef) {
            image.setPixel(x, y, 1f);
            
            consumeNeighbors(image,coef,x,y,points,aux);
            
            int minX = x, maxX = x, minY = y, maxY = y;
            
            while(!aux.isEmpty()) {
                SinglePoint p = aux.pop();
                
                if(p.x < minX)
                    minX = p.x;
                if(p.x > maxX)
                    maxX = p.x;
                if(p.y < minY)
                    minY = p.y;
                if(p.y > maxY)
                    maxY = p.y;
                
                consumeNeighbors(image,coef,p.x,p.y,points,aux);
            }
            
            return new Rectangle(minX,minY,maxX-minX+1,maxY-minY+1);
        } else
            return null;
    }
    
    private static void consumeNeighbors(ImageLayerData image, float coef, int x, int y, Vector<SinglePoint> points, Stack<SinglePoint> aux) {
        for(int px = -1; px <= 1; px++) {
            for(int py = -1; py <= 1; py++) {
                if(!(px == 0 && py == 0)) {
                    int nx = px+x;
                    int ny = py+y;
                    if(nx > 0 && nx < image.getWidth() && ny > 0 && ny < image.getHeight()) {
                        if(image.getPixel(nx, ny) < coef) {
                            SinglePoint point = new SinglePoint(nx,ny);
                            aux.push(point);
                            points.add(point);
                            image.setPixel(nx, ny, 1f);
                        }
                    }
                }
            }
        }
    }
}
